﻿#include "../exercise.h"

// READ: 函数模板 <https://zh.cppreference.com/w/cpp/language/function_template>
// TODO: 将这个函数模板化
// int plus(int a, int b) {
//     return a + b;
// }

// 将函数模板化
template<typename T>
T plus(T a, T b) {
    return a + b;
}

// 浮点数近似比较函数
template<typename T>
bool approximately_equal(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) {
    // 处理无穷大和NaN的情况
    if (std::isinf(a) || std::isinf(b)) {
        return a == b;
    }
    if (std::isnan(a) || std::isnan(b)) {
        return false;
    }
    
    // 近似比较：|a - b| <= epsilon * max(|a|, |b|, 1.0)
    T abs_a = std::abs(a);
    T abs_b = std::abs(b);
    T max_abs = (abs_a > abs_b) ? abs_a : abs_b;
    if (max_abs < 1.0) {
        max_abs = 1.0;
    }
    
    return std::abs(a - b) <= epsilon * max_abs;
}

int main(int argc, char **argv) {
    ASSERT(plus(1, 2) == 3, "Plus two int");
    ASSERT(plus(1u, 2u) == 3u, "Plus two unsigned int");

    // THINK: 浮点数何时可以判断 ==？何时必须判断差值？
    ASSERT(plus(1.25f, 2.5f) == 3.75f, "Plus two float");
    ASSERT(plus(1.25, 2.5) == 3.75, "Plus two double");
    // TODO: 修改判断条件使测试通过
    // ASSERT(plus(0.1, 0.2) == 0.3, "How to make this pass?");

    // 修改判断条件：使用近似比较而不是精确相等
    ASSERT(approximately_equal(plus(0.1, 0.2), 0.3), "How to make this pass?");

    return 0;
}


/*
好短

函数模板：template 

THINK: 浮点数何时可以判断 ==？何时必须判断差值？
0.1在二进制中是无限循环小数
0.2在二进制中也是无限循环小数
它们的和无法精确表示为二进制浮点数
因此存在微小的舍入误差




ai：
代码解释
1. 函数模板
template<typename T>
T plus(T a, T b) {
    return a + b;
}
使用template<typename T>声明模板
T是模板参数，可以是任何数值类型
函数支持int, unsigned int, float, double, long等各种数值类型
2. 浮点数近似比较
template<typename T>
bool approximately_equal(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) {
    // 处理特殊值
    if (std::isinf(a) || std::isinf(b)) return a == b;
    if (std::isnan(a) || std::isnan(b)) return false;
    
    // 相对误差比较
    T abs_a = std::abs(a);
    T abs_b = std::abs(b);
    T max_abs = (abs_a > abs_b) ? abs_a : abs_b;
    if (max_abs < 1.0) max_abs = 1.0;
    
    return std::abs(a - b) <= epsilon * max_abs;
}
3. 浮点数比较的思考
何时使用==？

整数值的浮点数：1.0 == 1.0
编译期常量：1.25 + 2.5 == 3.75
经过精确计算的数值
何时必须使用近似比较？

涉及浮点数运算的结果：0.1 + 0.2
经过多次运算的中间结果
从外部输入或计算得到的浮点数
4. 为什么0.1 + 0.2 != 0.3？
这是由于浮点数的二进制表示精度问题：

0.1在二进制中是无限循环小数
0.2在二进制中也是无限循环小数
它们的和无法精确表示为二进制浮点数
因此存在微小的舍入误差
更好的比较方法
对于生产代码，建议使用更健壮的比较方法：

#include <cmath>
#include <limits>

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
almost_equal(T a, T b, T rel_epsilon = 1e-8, T abs_epsilon = 1e-12) {
    T diff = std::abs(a - b);
    if (diff <= abs_epsilon) {
        return true;
    }
    T max_val = std::max(std::abs(a), std::abs(b));
    return diff <= rel_epsilon * max_val;
}
这种方法结合了相对误差和绝对误差比较，更加健壮。

测试验证
程序运行后会通过所有测试：

整数运算正确
无符号整数运算正确
浮点数运算使用适当的比较方法
0.1 + 0.2 ≈ 0.3 测试通过
这个实现提供了通用的加法模板函数，并正确处理了浮点数的精度问题。


*/